/*
   FieldVis - A Tool for Visualizing Astrophysical Magnetohydrodynamic Flow
   
   Copyright (C) 2006 - 2008     FieldVis Development Team
  
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
  
   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

   File        : Field.h
   Author      : Jeremy Moc  (mocj@msi.umn.edu)

   Description : The vector and scalar field interface to the data sets.
*/

#ifndef FIELD_H
#define FIELD_H

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <algorithm>
#include <map>
#include <cmath>

#include "DataSet.h"
#include "Point.h"
#include "Vector3D.h"


///////////////////////////////////////////////////////////////////////////////

class Field
{
public:
   Field(const int x, const int y, const int z)
      : xDim(x), yDim(y), zDim(z)
   {
      scaleFactor = (xDim >= zDim ? 
                     (xDim >= yDim ? xDim : yDim) :
                     (zDim >= zDim ? zDim : yDim));
   }

   ~Field()  {   }

   float GetScaleFactor() const { return (scaleFactor); }

   bool WasProperlyLoaded() const { return true; };

   int X() const { return (xDim); }
   int Y() const { return (yDim); }
   int Z() const { return (zDim); }
   
   bool InBounds(const int x, const int y, const int z) const
   {
     return ((x >= 0 && x < xDim) && (y >= 0 && y < yDim) && (z >= 0 && z < zDim));
   }

   bool InBounds(const Point &p) const
   {
   	int x = static_cast<int>(floor(p.GetX()));
   	int y = static_cast<int>(floor(p.GetY()));
   	int z = static_cast<int>(floor(p.GetZ()));

   	return InBounds(x, y, z);
   }

protected:

   int   xDim, yDim, zDim;
   float scaleFactor;
};

///////////////////////////////////////////////////////////////////////////////

class ScalarField : public Field
{
public:

   ScalarField(const std::string filename, const int x, const int y, const int z)
      : Field(x, y, z)
   {
      dataSet = DataSetManager::Instance().Manage(filename, x, y, z);
   }
   
   ~ScalarField()  {  }

   float GetGridPointValue(const int x, const int y, const int z) const
   {
//	if (!InBounds(x, y, z)) { /* maybe do something useful here !! */ }
      return (dataSet->Get(x, y, z));
   }

   float GetInterpolatedValue(const float x, const float y, const float z) const;

   float GetInterpolatedValue(const Point &p) const
   {
      return (GetInterpolatedValue(p.GetX(), p.GetY(), p.GetZ()));
   }

protected:

   DataSet *dataSet;
};

///////////////////////////////////////////////////////////////////////////////

class VectorField : public Field
{
public:

   VectorField(const std::string xFile, const std::string yFile, const std::string zFile,
	            const int x, const int y, const int z)
      : Field(x, y, z)
   {
      xField = DataSetManager::Instance().Manage(xFile, x, y, z);
      yField = DataSetManager::Instance().Manage(yFile, x, y, z);
      zField = DataSetManager::Instance().Manage(zFile, x, y, z);
   }

   ~VectorField() {  }
   
   float GetMagnitude(const float x, const float y, const float z) const
   {
      return (GetInterpolatedVector(x, y, z).Norm());
   }

   float GetMagnitude(const Point &p) const
   {
      return (GetMagnitude(p.GetX(), p.GetY(), p.GetZ()));
   }

   Vector3D GetGridPointVector(const int x, const int y, const int z) const
   {
//	if (!InBounds(x, y, z)) { /* maybe do something useful here !! */ }
      return (Vector3D(xField->Get(x, y, z), yField->Get(x, y, z), zField->Get(x, y, z)));
   }

   Vector3D GetInterpolatedVector(const float x, const float y, const float z) const;

   Vector3D GetInterpolatedVector(const Point &p) const
   {
      return (GetInterpolatedVector(p.GetX(), p.GetY(), p.GetZ()));
   }

protected:

   DataSet *xField, *yField, *zField;
};

#endif
